// Program: ata
// Description: Creates all ATA compatible block devices.
//

//
// Ata.h -- Defines the interface for a device representing an ATA disk.
//

#ifndef ATADEVICE_H
#define ATADEVICE_H

#include <jimix/Globals.h>
#include <jimix/resource.h>
#include <jimix/File.h>
#include "Map.h"
#include "Request.h"
#include "LinkedList.h"

#define DEFAULT_CONTROL_BASE 0x3F0
#define DEFAULT_COMMAND_BASE 0x1F0

#define HD_STATUS_ERROR                   0x00
#define HD_STATUS_INDEX                   0x02
#define HD_STATUS_READ_CORRECTED          0x04
#define HD_STATUS_BUFFER_NEEDS_SERVICING  0x08
#define HD_STATUS_SEEK_COMPLETE           0x10
#define HD_STATUS_WRITE_FAULT             0x20
#define HD_STATUS_READY                   0x40
#define HD_STATUS_BUSY                    0x80

typedef struct
{
  u8int active;
  u8int start_head;
  u8int start_cylinder_low;
  u8int start_cylinder_high;
  u8int type;
  u8int end_head;
  u8int end_cylinder_low;
  u8int end_cylinder_high;
  u32int start_lba;
  u32int size;
} PartitionInfo;

class AtaDevice : public Callable
{
  public:
    AtaDevice(u16int commandBase = DEFAULT_COMMAND_BASE,
              u16int controlBase = DEFAULT_CONTROL_BASE,
              u8int drive = 0);
    ~AtaDevice() {}
    
    u32int read(u32int offset, u32int size, u8int *buf);
    u32int write(u32int offset, u32int size, u8int *buf);
    
    // These are called via IPC.
    u32int requestRead(uint impl, uint offset, uint size, u8int *buf);
    u32int requestWrite(uint impl, uint offset, uint size, u8int *buf);
    void deviceReady();
    
    void getModelName(char *buf, int maxLen);
    u32int getCylinders();
    u32int getHeads() {return (u32int)ident[0x03];}
    u32int getBytesPerTrack() {return (u32int)ident[0x04];}
    u32int getBytesPerSector() {return (u32int)ident[0x05];}
    u32int getSectorsPerTrack() {return (u32int)ident[0x06];}
    void getSerialNumber(char *buf, int maxLen);
    u32int getBufferSize() {return (u32int)ident[0x15];}
    void getFirmwareRevision(char *buf, int maxLen);
    PartitionInfo getPartitionInfo(u32int physicalPartition)
    {
      return physPartitions[physicalPartition];
    }
    
    void identify();
    void readPartitionTable();
    /**
      Read a sector on disk, blocking until the read is complete.
    **/
    void readSectorBlock(u32int lba, u8int *buf);
    /**
      Read a number of sectors on disk, activating interrupts and not
      blocking.
    **/
    void readSectorNonBlock(u32int lba, u8int nSectors);
    /**
      Write a sector on disk, blocking until the write is complete.
    **/
    void writeSectorBlock(u32int lba, u8int *buf);
    /**
      Write a number of sectors on disk, activating interrupts and not
      blocking.
     **/
    void writeSectorNonBlock(u32int lba, u32int size, u8int *buf);
    /**
      Read sector buffer.
    **/
    void readBuffer(u32int size, u8int *buf);
    /**
      Makes sure the sector cache is clear.
    **/
    void clearCache();
    
    /**
      We are a remote resource.
    **/
    uint resource;
    
private:
    /**
      Maps a buffer location to a request object. Used for lookups.
    **/
    typedef Map<uint,Request*,17> RequestMap;
    /**
      The queue.
    **/
    typedef LinkedList<Request*> RequestQueue;
    /**
      The sector cache, indexed by LBA sector number.
    **/
    typedef Map<uint,u8int*,511> SectorCache;
  
    RequestMap requestMap;
    RequestQueue requestQueue;
    SectorCache sectorCache;
    Request *currentRequest;
    
    void dispatchRequest();
    
    u16int ident[256];
    u16int commandBase;
    u16int controlBase;
    u8int drive;
    PartitionInfo physPartitions[4];
};

#endif
